package cn.hinglo.common.plugin.quartz;

import cn.hinglo.permission.model.JobBean;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Db;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.List;

/***
 * 定时任务管理器
 * @author HingLo
 */
@Slf4j
public class QuartzManager {
    private Scheduler scheduler;
    @Getter
    @Setter
    private String confConfig;
    private static Boolean STATUS = Boolean.FALSE;
    private static QuartzManager quartzManager = new QuartzManager();

    /***
     * 任务调度器是个单例模式
     * @return 返回结果
     */
    public static QuartzManager getSingleton() {
        return quartzManager;
    }

    /***
     * 私有构造，保证任务管理器为单例
     */
    private QuartzManager(){

    }

    /***
     * 添加或者更新任务
     * @param job 任务类
     */
    public boolean addorUpdateJob(JobBean job) {
        if (!STATUS) {
            log.error("请先调用 QuartzManager.start() 方法初始化调度器");
            return false;
        }
        String taskName = getTaskName(job);
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(taskName, job.getGroupName());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 不存在，创建一个
            if (null == trigger) {
                Class<Job> jobs = (Class<Job>) Class.forName(job.getClazz());
                JobDetail jobDetail = JobBuilder.newJob(jobs).withIdentity(taskName, job.getGroupName()).build();
                jobDetail.getJobDataMap().put("scheduleJob", job);
                // 表达式调度构建器
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCron()).withMisfireHandlingInstructionDoNothing();
                // 按新的cronExpression表达式构建一个新的trigger
                trigger = TriggerBuilder.newTrigger().withIdentity(taskName, job.getGroupName())
                        .withSchedule(scheduleBuilder).build();
                try {
                    scheduler.scheduleJob(jobDetail, trigger);
                } catch (Exception e) {
                    e.printStackTrace();
                    return false;
                }
            } else {
                // 表达式如果没有变化，就不做更新处理
                if (trigger.getCronExpression().equals(job.getCron())) {
                    return true;
                }
                // Trigger已存在，那么更新相应的定时设置
                // 表达式调度构建器
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCron()).withMisfireHandlingInstructionDoNothing();
                // 按新的cronExpression表达式重新构建trigger
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
                // 按新的trigger重新设置job执行
                scheduler.rescheduleJob(triggerKey, trigger);

            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /***
     * 删除指定的任务
     * @param job job 任务信息实体
     * @return 是否删除成功
     */
    public boolean deleteJob(JobBean job) {
        try {
            TriggerKey tk = TriggerKey.triggerKey(getTaskName(job), job.getGroupName());
            // 停止触发器
            scheduler.pauseTrigger(tk);
            // 从调度器中删除trigger
            scheduler.unscheduleJob(tk);
            JobKey jobKey = JobKey.jobKey(getTaskName(job), job.getGroupName());
            //从调度器中删除任务
            scheduler.deleteJob(jobKey);
            return true;
        } catch (SchedulerException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 暂停任务
     *
     * @param job 任务方法
     * @return 是否暂停成
     */
    public boolean pauseJob(JobBean job) {
        try {
            TriggerKey tk = TriggerKey.triggerKey(getTaskName(job), job.getGroupName());
            Trigger trigger = scheduler.getTrigger(tk);
            if (trigger == null) {
                // 如果定时任务没有加载到内存中，就添加这个定时任务，并触发停止任务
                 addorUpdateJob(job);
            }
            scheduler.pauseTrigger(tk);
            JobKey jobKey = JobKey.jobKey(getTaskName(job), job.getGroupName());
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /***
     * 恢复作业
     * @param job 任务信息
     */
    public boolean resumeJob(JobBean job) {
        try {
            TriggerKey tk = TriggerKey.triggerKey(getTaskName(job), job.getGroupName());
            Trigger trigger = scheduler.getTrigger(tk);
            if (trigger == null) {
                // 如果定时任务没有加载到内存中，就添加这个定时任务
                return addorUpdateJob(job);
            }
            JobKey jobKey = JobKey.jobKey(getTaskName(job), job.getGroupName());
            scheduler.resumeJob(jobKey);
            scheduler.resumeTrigger(tk);
        } catch (SchedulerException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }


    /**
     * 启动调度器
     *
     * @return 返回是否启动成功
     */
    public boolean start() {
        try {
            if (!STATUS) {
                SchedulerFactory sf;
                if (StrKit.notBlank(confConfig)) {
                    sf = new StdSchedulerFactory(confConfig);
                } else {
                    sf = new StdSchedulerFactory();
                }
                scheduler = sf.getScheduler();
                scheduler.start();
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
            return false;
        }
        // 设置调度器成功启动
        STATUS = true;
        return true;
    }

    /***
     * 停止时调度器
     * @return 返回结果
     */
    public boolean stop() {
        if (STATUS) {
            try {
                scheduler.shutdown();
            } catch (SchedulerException e) {
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }

    /***
     * 从数据中获取所有待运行状态的任务初始化调度器的工厂
     */
    public void initTask() {
        // 获取数据库中的全部任务，然后进行装填到调度器中
        String sql = Db.getSql("job.run");
        List<JobBean> list = JobBean.dao.find(sql);
        list.forEach(this::addorUpdateJob);
    }

    /***
     * 返回job与trigger的名称，使用任务名称与id共同组建，防止相同任务隶属多个trigger的情况
     * @param job 任务对象
     * @return 返回任务或者trigger的名称
     */
    private String getTaskName(JobBean job) {
        return job.getName() + job.getId();
    }

}
